﻿using Asplode;
using Asplode.GameEntity.Weapons;
using Microsoft.Xna.Framework;
using Asplode.GameEntity.Characters;
using BEPUphysics.Entities;
using Asplode.GameWorld;
using System;

namespace FlyingGamePC.GameEntity.Weapons
{
    public class Mortar : Weapon
    {
        public Mortar()
            : base("mortar", 100, -1, float.PositiveInfinity)
        {
        }

        private double getShotRange(double startHeight, double vx, double vy, double accel)
        {
            //vf^2 = vo^2 + 2*a*d
            //vf = vo+ at
            // t = (sqrt(vo^2 + 2*a*d)-vo)/a
            double timeDown = (Math.Sqrt(vy * vy + 2 * -accel * startHeight)-vy) / -accel;

            //vo = vi + at
            // at the end of the parabola, vf = - vo
            // t = 2 * -vo /t
            double parabolaTime = -2 * vy / accel;

            return (timeDown + parabolaTime) * vx;

        }

        private Vector3 getShotAngleVector(Vector3 startPos, Vector3 aimPos, float power)
        {
            
            Vector3 shotDir = aimPos-startPos;
            shotDir.Y=0;
            double desiredRange = shotDir.Length();
            
            double thetaMax = Math.PI / 2;
            double thetaMaxD = 0;

            double thetaMin = Math.PI / 4;
            double thetaMinD;
            


            double vx = power * Math.Cos(thetaMin);
            double vy = power * Math.Sin(thetaMin);

            thetaMinD = getShotRange(startPos.Y, vx, vy, -9.81f);


            //16 iteration approximation 
            for (int i = 0; i < 30; i++)
            {
                double thetaLocal = (thetaMax + thetaMin) / 2;

                vx = power * Math.Cos(thetaLocal);
                vy = power * Math.Sin(thetaLocal);
                double thetaLocalRange = getShotRange(startPos.Y, vx, vy, -9.81f);

                if (desiredRange > thetaLocalRange)
                {
                    thetaMax = thetaLocal;
                    thetaMaxD = thetaLocalRange;
                }
                else
                {
                    thetaMin = thetaLocal;
                    thetaMinD = thetaLocalRange;
                }
            }


            shotDir.Normalize();
            shotDir.Y = (float)Math.Tan((thetaMax+thetaMin)/2);

            shotDir.Normalize();

            return shotDir;
        }

        public override void Shoot(Character character, Character target, GameWorld.EntityFactory entityFactory)
        {
            //rotation = character.rotation;

            // The character's rotation vector has a Y component of 0 and is
            // normalized, so this will always shoot at a 45 degree angle.
            //Vector3 shotAngle = character.weapon.rotationVector + Vector3.UnitY;
            
            Entity characterBody = character.gameObject.body;
            Vector3 shotAngle = getShotAngleVector( //weapon is ontop of player
                characterBody.centerOfMass + characterBody.maximumRadius * Vector3.Up,
                (character as Human).Cursor.centerOfMass, // target
                15);//shooting power
            shotAngle.Normalize();

            //jitter the shotAngle to make mortars hard to use
            Random random = new Random();
            float jitter = -.05f;

            shotAngle.X *= (float)(1 + (random.NextDouble() * jitter));
            shotAngle.Z *= (float)(1 + (random.NextDouble() * jitter));
            shotAngle.Y *= (float)(1 + (random.NextDouble() * jitter));

            
            Vector3 origin = characterBody.centerOfMass + shotAngle * (characterBody.maximumRadius + 1);

            Vector3 initialAcceleration = shotAngle * 15f;

            Entity mortarShot = entityFactory.createMortarShell(origin);

            EventDispatcher.Instance.RaiseMortarShot(mortarShot, initialAcceleration);

            // Decrement ammo and figure out when we can shoot next.
            FinishShot();
        }
    }
}
